﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.MountAndBlade.View.Scripts.PopupSceneCameraPath
// Assembly: TaleWorlds.MountAndBlade.View, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 61152EB8-513B-4D4A-8AC0-0D1465C6CF6C
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\Modules\Native\bin\Win64_Shipping_Client\TaleWorlds.MountAndBlade.View.dll

using System;
using System.Collections.Generic;
using TaleWorlds.DotNet;
using TaleWorlds.Engine;
using TaleWorlds.Library;

#nullable disable
namespace TaleWorlds.MountAndBlade.View.Scripts
{
  public class PopupSceneCameraPath : ScriptComponentBehavior
  {
    public string LookAtEntity = "";
    public string SkeletonName = "";
    public int BoneIndex;
    public Vec3 AttachmentOffset = new Vec3();
    public string InitialPath = "";
    public string InitialAnimationClip = "";
    public string InitialSound = "event:/mission/siege/siegetower/doorland";
    public float InitialPathStartTime;
    public float InitialPathDuration = 1f;
    public PopupSceneCameraPath.InterpolationType InitialInterpolation;
    public bool InitialFadeOut;
    public string PositivePath = "";
    public string PositiveAnimationClip = "";
    public string PositiveSound = "";
    public float PositivePathStartTime;
    public float PositivePathDuration = 1f;
    public PopupSceneCameraPath.InterpolationType PositiveInterpolation;
    public bool PositiveFadeOut;
    public string NegativePath = "";
    public string NegativeAnimationClip = "";
    public string NegativeSound = "";
    public float NegativePathStartTime;
    public float NegativePathDuration = 1f;
    public PopupSceneCameraPath.InterpolationType NegativeInterpolation;
    public bool NegativeFadeOut;
    private bool _isReady;
    public SimpleButton TestInitial;
    public SimpleButton TestPositive;
    public SimpleButton TestNegative;
    private MatrixFrame _localFrameIdentity = MatrixFrame.Identity;
    private GameEntity _lookAtEntity;
    private int _currentState;
    private float _cameraFadeValue;
    private List<PopupSceneSkeletonAnimationScript> _skeletonAnims = new List<PopupSceneSkeletonAnimationScript>();
    private SoundEvent _activeSoundEvent;
    private readonly PopupSceneCameraPath.PathAnimationState[] _transitionState = new PopupSceneCameraPath.PathAnimationState[3];

    protected override void OnInit()
    {
      base.OnInit();
      this.SetScriptComponentToTick(this.GetTickRequirement());
    }

    protected override void OnEditorInit() => base.OnEditorInit();

    public void Initialize()
    {
      if (this.SkeletonName != "" && ((NativeObject) this.GameEntity.Skeleton == (NativeObject) null || this.GameEntity.Skeleton.GetName() != this.SkeletonName))
        this.GameEntity.CreateSimpleSkeleton(this.SkeletonName);
      else if (this.SkeletonName == "" && (NativeObject) this.GameEntity.Skeleton != (NativeObject) null)
        this.GameEntity.RemoveSkeleton();
      if (this.LookAtEntity != "")
        this._lookAtEntity = this.GameEntity.Scene.GetFirstEntityWithName(this.LookAtEntity);
      this._transitionState[0].path = this.InitialPath == "" ? (Path) null : this.GameEntity.Scene.GetPathWithName(this.InitialPath);
      this._transitionState[0].animationName = this.InitialAnimationClip;
      this._transitionState[0].startTime = this.InitialPathStartTime;
      this._transitionState[0].duration = this.InitialPathDuration;
      this._transitionState[0].interpolation = this.InitialInterpolation;
      this._transitionState[0].fadeCamera = this.InitialFadeOut;
      this._transitionState[0].soundEvent = this.InitialSound;
      this._transitionState[1].path = this.PositivePath == "" ? (Path) null : this.GameEntity.Scene.GetPathWithName(this.PositivePath);
      this._transitionState[1].animationName = this.PositiveAnimationClip;
      this._transitionState[1].startTime = this.PositivePathStartTime;
      this._transitionState[1].duration = this.PositivePathDuration;
      this._transitionState[1].interpolation = this.PositiveInterpolation;
      this._transitionState[1].fadeCamera = this.PositiveFadeOut;
      this._transitionState[1].soundEvent = this.PositiveSound;
      this._transitionState[2].path = this.NegativePath == "" ? (Path) null : this.GameEntity.Scene.GetPathWithName(this.NegativePath);
      this._transitionState[2].animationName = this.NegativeAnimationClip;
      this._transitionState[2].startTime = this.NegativePathStartTime;
      this._transitionState[2].duration = this.NegativePathDuration;
      this._transitionState[2].interpolation = this.NegativeInterpolation;
      this._transitionState[2].fadeCamera = this.NegativeFadeOut;
      this._transitionState[2].soundEvent = this.NegativeSound;
      SoundManager.SetListenerFrame(MatrixFrame.Identity with
      {
        origin = this.GameEntity.GlobalPosition
      });
      List<GameEntity> entities = new List<GameEntity>();
      this.Scene.GetAllEntitiesWithScriptComponent<PopupSceneSkeletonAnimationScript>(ref entities);
      entities.ForEach((Action<GameEntity>) (e => this._skeletonAnims.Add(e.GetFirstScriptOfType<PopupSceneSkeletonAnimationScript>())));
      this._skeletonAnims.ForEach((Action<PopupSceneSkeletonAnimationScript>) (s => s.Initialize()));
    }

    private void SetState(int state)
    {
      if ((NativeObject) this.GameEntity.Skeleton != (NativeObject) null && !string.IsNullOrEmpty(this._transitionState[state].animationName))
        this.GameEntity.Skeleton.SetAnimationAtChannel(this._transitionState[state].animationName, 0);
      this._currentState = state;
      this._transitionState[state].alpha = 0.0f;
      if ((NativeObject) this._transitionState[state].path != (NativeObject) null)
        this._transitionState[state].totalDistance = this._transitionState[state].path.GetTotalLength();
      if (this._transitionState[state].soundEvent != "")
      {
        this._activeSoundEvent?.Stop();
        this._activeSoundEvent = SoundEvent.CreateEventFromString(this._transitionState[state].soundEvent, (Scene) null);
        if (this._isReady)
          this._activeSoundEvent?.Play();
      }
      this.UpdateCamera(0.0f, ref this._transitionState[state]);
      this._skeletonAnims.ForEach((Action<PopupSceneSkeletonAnimationScript>) (s => s.SetState(state)));
    }

    public void SetInitialState() => this.SetState(0);

    public void SetPositiveState() => this.SetState(1);

    public void SetNegativeState() => this.SetState(2);

    public void SetIsReady(bool isReady)
    {
      if (this._isReady == isReady)
        return;
      if (isReady)
      {
        SoundEvent activeSoundEvent = this._activeSoundEvent;
        if ((activeSoundEvent != null ? (!activeSoundEvent.IsPlaying() ? 1 : 0) : 0) != 0)
          this._activeSoundEvent.Play();
      }
      this._isReady = isReady;
    }

    public float GetCameraFade() => this._cameraFadeValue;

    public void Destroy()
    {
      this._activeSoundEvent?.Stop();
      for (int index = 0; index < 3; ++index)
        this._transitionState[index].path = (Path) null;
    }

    private float InQuadBlend(float t) => t * t;

    private float OutQuadBlend(float t) => t * (2f - t);

    private float InOutQuadBlend(float t)
    {
      return (double) t >= 0.5 ? (float) ((4.0 - 2.0 * (double) t) * (double) t - 1.0) : 2f * t * t;
    }

    private MatrixFrame CreateLookAt(Vec3 position, Vec3 target, Vec3 upVector)
    {
      Vec3 vec3_1 = target - position;
      double num1 = (double) vec3_1.Normalize();
      Vec3 va = Vec3.CrossProduct(vec3_1, upVector);
      double num2 = (double) va.Normalize();
      Vec3 vec3_2 = Vec3.CrossProduct(va, vec3_1);
      return new MatrixFrame(va.x, va.y, va.z, 0.0f, vec3_2.x, vec3_2.y, vec3_2.z, 0.0f, -vec3_1.x, -vec3_1.y, -vec3_1.z, 0.0f, position.x, position.y, position.z, 1f);
    }

    private float Clamp(float x, float a, float b)
    {
      if ((double) x < (double) a)
        return a;
      return (double) x <= (double) b ? x : b;
    }

    private float SmoothStep(float edge0, float edge1, float x)
    {
      x = this.Clamp((float) (((double) x - (double) edge0) / ((double) edge1 - (double) edge0)), 0.0f, 1f);
      return (float) ((double) x * (double) x * (3.0 - 2.0 * (double) x));
    }

    private void UpdateCamera(float dt, ref PopupSceneCameraPath.PathAnimationState state)
    {
      GameEntity entityWithTag = this.GameEntity.Scene.FindEntityWithTag("camera_instance");
      if ((NativeObject) entityWithTag == (NativeObject) null)
        return;
      state.alpha += dt;
      if ((double) state.alpha > (double) state.startTime + (double) state.duration)
        state.alpha = state.startTime + state.duration;
      float t = this.SmoothStep(state.startTime, state.startTime + state.duration, state.alpha);
      switch (state.interpolation)
      {
        case PopupSceneCameraPath.InterpolationType.EaseIn:
          t = this.InQuadBlend(t);
          break;
        case PopupSceneCameraPath.InterpolationType.EaseOut:
          t = this.OutQuadBlend(t);
          break;
        case PopupSceneCameraPath.InterpolationType.EaseInOut:
          t = this.InOutQuadBlend(t);
          break;
      }
      state.easedAlpha = t;
      if (state.fadeCamera)
        this._cameraFadeValue = t;
      if ((NativeObject) this.GameEntity.Skeleton != (NativeObject) null && !string.IsNullOrEmpty(state.animationName))
      {
        MatrixFrame parent = this._localFrameIdentity.TransformToParent(this.GameEntity.Skeleton.GetBoneEntitialFrame((sbyte) this.BoneIndex));
        MatrixFrame frame = new MatrixFrame()
        {
          rotation = parent.rotation
        };
        frame.rotation.u = -parent.rotation.s;
        frame.rotation.f = -parent.rotation.u;
        frame.rotation.s = parent.rotation.f;
        frame.origin = parent.origin + this.AttachmentOffset;
        entityWithTag.SetFrame(ref frame);
        SoundManager.SetListenerFrame(frame);
      }
      else
      {
        if (!((NativeObject) state.path != (NativeObject) null))
          return;
        float distance = t * state.totalDistance;
        Vec3 origin = state.path.GetFrameForDistance(distance).origin;
        MatrixFrame frame = MatrixFrame.Identity;
        if ((NativeObject) this._lookAtEntity != (NativeObject) null)
          frame = this.CreateLookAt(origin, this._lookAtEntity.GetGlobalFrame().origin, Vec3.Up);
        else
          frame.origin = origin;
        entityWithTag.SetGlobalFrame(in frame);
      }
    }

    public override ScriptComponentBehavior.TickRequirement GetTickRequirement()
    {
      return ScriptComponentBehavior.TickRequirement.Tick | base.GetTickRequirement();
    }

    protected override void OnTick(float dt)
    {
      this.UpdateCamera(dt, ref this._transitionState[this._currentState]);
    }

    protected override void OnEditorTick(float dt)
    {
      base.OnEditorTick(dt);
      this.OnTick(dt);
    }

    protected override void OnEditorVariableChanged(string variableName)
    {
      base.OnEditorVariableChanged(variableName);
      this.Initialize();
      if (variableName == "TestInitial")
        this.SetState(0);
      if (variableName == "TestPositive")
        this.SetState(1);
      if (!(variableName == "TestNegative"))
        return;
      this.SetState(2);
    }

    public enum InterpolationType
    {
      Linear,
      EaseIn,
      EaseOut,
      EaseInOut,
    }

    public struct PathAnimationState
    {
      public Path path;
      public string animationName;
      public float totalDistance;
      public float startTime;
      public float duration;
      public float alpha;
      public float easedAlpha;
      public bool fadeCamera;
      public PopupSceneCameraPath.InterpolationType interpolation;
      public string soundEvent;
    }
  }
}
